/*
 * boidtype.c
 *
 *  Created on: June 26, 2011
 *      Authors: Lane Aasen, Nikholas Gaffney, Michael Rosenberger, Dylan Swiggett
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
#include <ctype.h>

#define FSS		1049576 	/*bytes allocated to the file buffer which contains the entire file (currently 1Mb or ~10000 BoidTypes)*/
#define EBS		256		/*bytes allocated to the regex error buffer*/
#define NS		64		/*bytes allocated to the name field*/
#define DS		4096		/*bytes allocated to the data field which contains all field declarations besides name*/
#define FS		64		/*bytes allocated to the field reference*/
#define VS		64		/*bytes allocated to the value of each field*/
#define NMN		3		/*subexpressions + 1 in the name regex*/
#define NMFV	3			/*subexpressions + 1 in the field/value regex*/
#define DEFR	0 			/*default red value*/
#define DEFG	0
#define DEFB	0
#define DEFA	255			/*default alpha value (opaque)*/
#define DEFD	1			/*default density*/
#define DEFAR	14			/*default attraction radius*/
#define DEFRR	10			/*default repulsion radius*/
#define DEFAS	.1			/*default attraction strength*/
#define DEFRS	.17

typedef struct {
	int r, g, b;
	float a;
} Color;

typedef struct {
	char* name;
	double density;
	Color color;
	double attrRadius, repelRadius;
	double attrStrength, repelStrength;
} BoidType;

typedef struct {
	struct Node* next;
	BoidType current;
} Node;

BoidType* initBoidTypes(char fileName[]) {
	FILE *file = fopen(fileName, "r");
	if (file == 0) {
		perror("BoidType initialization file did not open correctly.\nPlease make sure that the file exists and is in a directory you can access.\n");
	}

	/*regex error buffer*/
	char* errBuf = malloc(sizeof(char));

	/*name regex*/
	/*matches an alphanumeric name followed by a '{', arbitrary data (presumably field declarations), and finally a '}'*/
	/*e.g. "iron{...}"*/
	regex_t nameRgx; 
	int nameRgxS = regcomp(&nameRgx, "[\"\']?([A-Za-z0-9_]+?)[\'\"]?[{]([^{]*)[}]", REG_EXTENDED); /*greedy modifiers not working, workaround used*/
	if (nameRgxS != 0) {
		regerror(nameRgxS, &nameRgx, errBuf, EBS);
		printf("Name regex failed at the time of compilation due to: %s\n", errBuf);
	}

	/*field and value regex*/
	/*matches alphanumeric field identifier followed by a value surrounded by parens or angle brackets*/
	/*e.g. "r(250)" or "r<250>"*/
	regex_t fvalRgx; 
	int fvalRgxS = regcomp(&fvalRgx, "[\'\"]?([A-Za-z0-9_]+)[[\'\"]?[(<]([A-Za-z0-9.]+)[<)]", REG_EXTENDED);
	if (fvalRgxS != 0) {
		regerror(fvalRgxS, &fvalRgx, errBuf, EBS);
		printf("Field/value regex failed at the time of compilation due to: %s\n", errBuf);
	}

	free(errBuf); /*regexes compiles; error buffer no longer needed*/

	/*when a regex is applied to text it returns an array of start and stop indeces for each match*/
	/*the length of this array is determined by the number of subexpressions in the regex*/
	int nmatchN = 3; /*number of subexpressions in nameRgx*/
	regmatch_t* matchN = malloc(sizeof(regmatch_t) * nmatchN); 

	int nmatchFV = 3; /*number of subexpressions in the field/value regex*/
	regmatch_t* matchFV = malloc(sizeof(regmatch_t) * nmatchFV);

	/*read file into a large string buffer*/
	char* fs = malloc(sizeof(char) * FSS); /*string file buffer*/

	int fi = 0; /*file index*/
	/*read file, character by character, into the file string*/
	while(!feof(file) && fi < FSS) {
		fs[fi] = fgetc(file);
		fi++;
	}
	/*make a pointer that is identical to fs*/
	/*the value of this pointer is incremented once portions of text are read*/
	/*into boid types so that text is not matched multiple times*/
	/*fs is then freed at the end*/
	char* fstemp = fs;

	/*array of BoidTypes*/
	/*TODO: arrays with dynamic length (glib)*/
	BoidType* bts = malloc(sizeof(BoidType) * 1024);
	/*current number of boid types*/	
	int numbt = 0;
	/*status code from regex execution (0 is match)*/
	int statusN = regexec(&nameRgx, fstemp, nmatchN, matchN, 0);
	while(!statusN) {
		char* name = malloc(NS);
		memset(name, 0, NS); /*strncpy does not always append a null termination character to the end of a copied string; (TODO: appending one would be faster)*/
		strncpy(name, fstemp + matchN[1].rm_so, matchN[1].rm_eo - matchN[1].rm_so);

		/*data is the text in between curly braces i.e. [name]{[data]}*/
		char* data = malloc(DS);
		strncpy(data, fstemp + matchN[2].rm_so, matchN[2].rm_eo - matchN[2].rm_so);
		char* datatemp = data;

		/*new BoidType made with default values and provided name*/
		Color tempC = {DEFR, DEFG, DEFB, DEFA};
		BoidType tempBT = {name, DEFD, tempC, DEFAR, DEFRR, DEFAS, DEFRS};
		int statusFV = regexec(&fvalRgx, datatemp, nmatchFV, matchFV, 0); /*regex execution status of field-value regex*/		
		while(!statusFV) {
			char* field = malloc(FS);
			memset(field, 0, sizeof(char) * FS);
			strncpy(field, datatemp + matchFV[1].rm_so, matchFV[1].rm_eo - matchFV[1].rm_so);

			char* value = malloc(VS);
			memset(value, 0, sizeof(char) * VS);
			strncpy(value, datatemp + matchFV[2].rm_so, matchFV[2].rm_eo - matchFV[2].rm_so);

			/*printf("strcmp: %i\n", strcmp("r", "r") == 0);*/
			/*TODO: replace literal string equality test with fuzzy regex or something of the sort*/
			printf("(%s, %f)\n", field, atof(value));
			if(strcmp(field, "r") == 0) tempC.r = (int) atof(value);
			else if(strcmp(field, "g") == 0) tempC.g = (int) atof(value);
			else if(strcmp(field, "b") == 0) tempC.b = (int) atof(value);
			else if(strcmp(field, "a") == 0) tempC.a = atof(value);
			else if(strcmp(field, "d") == 0) tempBT.density = atof(value);
			else if(strcmp(field, "ar") == 0) tempBT.attrRadius = atof(value);
			else if(strcmp(field, "rr") == 0) tempBT.repelRadius = atof(value);
			else if(strcmp(field, "as") == 0) tempBT.attrStrength = atof(value);
			else if(strcmp(field, "rs") == 0) tempBT.repelStrength = atof(value);

			datatemp+=matchFV[0].rm_eo;

			statusFV = regexec(&fvalRgx, datatemp, nmatchFV, matchFV, 0);

			free(field);
			free(value);
		}

		fstemp+=matchN[0].rm_eo;
		statusN = regexec(&nameRgx, fstemp, nmatchN, matchN, 0);

		/*bts = realloc(bts, sizeof(BoidType) * numbt + 1);*/
		/*if(bts == NULL) printf("ARCSim ran out of memory while parsing BoidTypes.");*/
		bts[numbt] = tempBT;
		numbt++;

		/*statusN = regexec(&nameRgx, fstemp, nmatchN, matchN, 0); //program stops here for some reason =/*/

		/*free(name); //freeing here creates major derpage*/
		free(data);
	}

	/*cleanup*/
	regfree(&nameRgx);
	regfree(&fvalRgx);
	free(matchN);
	free(matchFV);
	free(fs);

	int success = fclose(file);
	if (success != 0) {
		perror("BoidType initialization file did not close successfully.\n");
	}

	/*Untested code for testing*/
	printf("Number of BoidTypes: %d\n", numbt);
	int i;
	for(i = 0; i < numbt; i++) {
		printf("===%s===\n", bts[i].name);
		printf("RGBA: (%d, %d, %d, %f)\n", bts[i].color.r, bts[i].color.g, bts[i].color.b, bts[i].color.a);
		printf("D: %f\n", bts[i].density);
		printf("AR: %f, AS: %f\n", bts[i].attrRadius, bts[i].attrStrength);
		printf("RR: %f, RS: %f\n\n", bts[i].repelRadius, bts[i].repelStrength);
	}

	return(bts);
	/*free(bts);*/
}




